package com.xiaobaibai.security.token.filter;

import com.xiaobaibai.common.response.ResponseCode;
import com.xiaobaibai.config.MyException;
import com.xiaobaibai.security.token.token.JwtAuthenticationToken;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
import org.springframework.security.web.util.matcher.*;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@Slf4j
public class JwtAuthenticationFilter extends AbstractAuthenticationProcessingFilter {

    /** todo  必须记住点
     * AbstractAuthenticationProcessingFilter是处理form登陆的过滤器,与 form 登陆有关的所有操作都是在该类及其子类中进行的。
     * 因为这个filter默认是管登录的,就是说不管你是访问什么url,它最终不会到你的访问url上,
     * 而是到>>>successHandler<<<就终止了
     * 所以呢?我们用这个当作资源访问过滤器是不符号场景的!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
     * 我们根据它的子类UsernamePasswordAuthenticationFilter也可以判断出这一点来~~~
     * todo 继承的过滤器不符号场景,废弃掉本类,可以去看No1的过滤器
     */



    /** todo
     * 易懵点:要知道security的filter不是用于检验的,
     *       它是将请求信息(筛选url后)递交给providerManger,然后开始security验证流程。
     *
     * 思考中:之前spring-security教学项目中的sms验证
     * 它准备两个filter:
     * 1.原生的filter,用于检验请求中是否带有短信验证码,然后判断验证码是否正确
     * 2.security自带的filter,用于搭建security的验证流程,并未在里面做判断
     * 3.我又想到有个provider,那为什么要在原生filter里面验证请求验证码是否正确呢?
     *
     * 然后我现在想的是,我不想这么麻烦,我的流程就只有两步
     * 1.security的filter筛选对应url(事实证明源码里面的filter也只是做给桥梁)
     * 2.在provider里面检验请求是否合法
     * 这样就简便多了,开始执行
     *
     * todo 上方想法开始解答:现在做实验,判断是不是只要是关于security原生的filter都是关于登录的,不会涉及到访问资源验证呢?
     * 去No3Filter查看即可
     *
     */

    public JwtAuthenticationFilter() {
        super(new AntPathRequestMatcher("/getUserInfo"));
    }

    @Override
    public Authentication attemptAuthentication(HttpServletRequest request,
                                                HttpServletResponse response) throws AuthenticationException, IOException, ServletException {

        log.info("tokenFilter拦截的uri:(仅设置了支持/getUserInfo)"+request.getRequestURI());

        //获取jwt
        String jwtToken=getTokenByRequest(request);

        JwtAuthenticationToken jwtAuthenticationToken =
                new JwtAuthenticationToken(jwtToken);//第一个放入jwt

        this.setDetails(request, jwtAuthenticationToken);
        return this.getAuthenticationManager().authenticate(jwtAuthenticationToken);
    }

    protected void setDetails(HttpServletRequest request, JwtAuthenticationToken authRequest) {
        authRequest.setDetails(this.authenticationDetailsSource.buildDetails(request));
    }

    public String getTokenByRequest(HttpServletRequest request) {

        String headerToken=request.getHeader("X-Token");
        log.info("heard里面的token:"+headerToken);
        //开始基于JWT的验证

        if(StringUtils.isBlank(headerToken)){
            //返回请携带令牌
            throw new MyException("请携带令牌");
        }

        return headerToken;
    }

}
